<!DOCTYPE html>
<!--[if lt IE 7]>       <html class="no-js ie6" lang="en"> <![endif]-->
<!--[if IE 7]>          <html class="no-js ie7" lang="en"> <![endif]-->
<!--[if IE 8]>          <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if gt IE 8]><!-->  <html class="no-js" lang="en" manifest="slides.appcache"> <!--<![endif]-->
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  
  <title>Chrome &amp; HTML5 &raquo; Building Applications for the Web</title>
  
  <meta name="description" content="A jQuery library for modern HTML presentations" />
  <meta name="author" content="Caleb Troughton" />
  <meta name="viewport" content="width=960" />
  
  <!-- Core and extension CSS files -->
  <link rel="stylesheet" href="core/deck.core.css" />
  <link rel="stylesheet" href="css/standard.css" />
  <link rel="stylesheet" href="css/prettify.css" />
  
  <link rel="stylesheet" href="extensions/goto/deck.goto.css" />
  <link rel="stylesheet" href="extensions/menu/deck.menu.css" />
  <link rel="stylesheet" href="extensions/navigation/deck.navigation.css" />
  <link rel="stylesheet" href="extensions/status/deck.status.css" />
  <link rel="stylesheet" href="extensions/hash/deck.hash.css" />
  
  <link rel="stylesheet" id="transition-theme-link" href="themes/transition/horizontal-slide.css" />

  <!-- Deck theme -->
  <link rel="stylesheet" id="style-theme-link" href="themes/style/g-africa.css" />

  <!-- Deck-specific CSS -->
  <link rel="stylesheet" href="css/chrome-and-html5.css" />
  
  <script src="js/prettify.js"></script>
  <script src="js/modernizr.custom.js"></script>
</head>

<body class="home">
  
<article>
  <section class="slide deck-current" id="intro">
    <div>
      <hgroup>
        <h1>Chrome <span class="amp">&amp;</span> HTML5</h1>
        <h2 class="insert-text shifted">Awesome!</h2>
        <h2>Building<span class="insert-text sub">^</span>Applications for the Web</h2>
      </hgroup>
    </div>
  </section>

  <section class="slide deck-after" id="presenters">
    <hgroup>
      <h1>Us</h1>
      <h2>Who are these guys anyhow?</h2>
    </hgroup>
    <div>
      <h1>Bob Aman</h1>
      <h1>Paa Kwesi Imbeah</h1>
    </div>
  </section>

  <section class="slide deck-next" id="labs">
    <hgroup>
      <h1>Code Labs</h1>
      <h2>What we're going to build together!</h2>
    </hgroup>
    <div>
      <div class="accordion">
        <section class="slide" id="offline-lab">
          <h1>Offline Applications</h1>
          <p>When you're traveling, it's useful to have ticket information handy. But you might not have internet access at the airport! We'll be building a web application that can store ticket information and operate while offline.</p>
        </section>
        <section class="slide" id="multi-device-lab">
          <h1>Multi-Device Designs</h1>
          <p>It's great that we can build an offline web application, but who really wants to pull out their laptop at the airport ticket counter? It'd be way better if you could use your mobile phone to check your ticket information.</p>
        </section>
        <section class="slide" id="cws-lab">
          <h1>Packaging for Chrome Web Store</h1>
          <p>Great! And now that we've made our application work well while offline and on mobile devices, now we want people to be able to discover it. We should package it up and publish it on the Chrome Web Store!</p>
        </section>
      </div>
    </div>
  </section>

  <section class="slide deck-after" id="download">
    <hgroup>
      <h1>Before We Start</h1>
      <h2>What you're going to need!</h2>
    </hgroup>
    <div>
      <h1>
        <a href="http://g-africa-codelab.appspot.com/">
          g-africa-codelab.appspot.com
        </a>
      </h1>
      <h2>Download the code lab here!</h2>
    </div>
  </section>


  <section class="slide deck-after" id="manifest-attribute">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        The most important feature of an offline application is the cache
        manifest. It tells the browser what to cache for offline use.
      </p>
      <pre class="prettyprint">&lt;html manifest="example.appcache"&gt;
  ...
&lt;/html&gt;</pre>
      <p>
        It's simply an external resource you specify with an attribute on your
        <code class="prettyprint">&lt;html&gt;</code> element.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="manifest-format">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        A manifest resource has three sections, and looks like this:
      </p>
      <pre class="prettyprint">
CACHE MANIFEST
CACHE:
# Cached files go here
FALLBACK:
# Replacements to serve when offline
NETWORK:
# Resources that require online access</pre>
    </div>
  </section>

  <section class="slide deck-after" id="manifest-content-type">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        A manifest <strong>must</strong> be served with a
        <code>Content-Type</code> of <code>text/cache-manifest</code>.
      </p>
      <p>
        Most web servers will try to serve it as
        <code>application/octet-stream</code> or
        <code>text/plain</code> and that won't work.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="appcache-scripting">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        The application cache can be accessed programmatically via the
        <code class="prettyprint">window.applicationCache</code> variable.
      </p>
      <dl>
        <dt>To force an update:</dt>
        <dd>
          <code class="prettyprint">
            window.applicationCache.update()
          </code>
        </dd>
        <dt>To check status:</dt>
        <dd>
          <code class="prettyprint">
            window.applicationCache.status
          </code>
        </dd>
        <dt>To handle events:</dt>
        <dd>
          <code class="prettyprint">
            window.applicationCache.addEventListener()
          </code>
        </dd>
      </dl>
    </div>
  </section>

  <section class="slide deck-after" id="appcache-events">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        The application cache may fire a number of different events:
      </p>
      <ul>
        <li><code class="prettyprint">"cached"</code></li>
        <li><code class="prettyprint">"checking"</code></li>
        <li><code class="prettyprint">"downloading"</code></li>
        <li><code class="prettyprint">"error"</code></li>
        <li><code class="prettyprint">"noupdate"</code></li>
        <li><code class="prettyprint">"obsolete"</code></li>
        <li><code class="prettyprint">"progress"</code></li>
        <li><code class="prettyprint">"updateready"</code></li>
      </ul>
    </div>
  </section>

  <section class="slide deck-after" id="appcache-event-handling">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        To respond to any of those events:
      </p>
      <pre class="prettyprint">
window.applicationCache.addEventListener(
  "updateready", function(event) {
    // Your code goes here
  }
);</pre>
    </div>
  </section>

  <section class="slide deck-after" id="appcache-tips">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        Some tips:
      </p>
      <ul>
        <li>
          You may want to disable any server-side caching of the manifest while
          debugging.
        </li>
        <li>
          Put a version number in a comment at the top of the manifest.
          Incrementing will force a refresh.
        </li>
        <li>
          Consider logging most application cache events to the console
          when debugging. Some logging happens automatically in Chrome.
        </li>
        <li>
          Don't bother trying to test from <code>file:///</code> URIs.
          It doesn't work.
        </li>
      </ul>
    </div>
  </section>

  <section class="slide deck-after" id="extra-credit">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <h1>Extra Credit</h1>
      <p>
        Figure out which resources this application will need to cache for
        offline usage. 
      </p>
      <p>
        <strong>Tip:</strong> Check Chrome's network tab in the developer tools
        to see what requests are being made. Generally you'll need to cache
        anything the browser is requesting.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-about">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        The application cache manifest does a great job handling static data.
        But you may need to handle dynamic data while offline.
      </p>
      <p>
        That's what <strong>IndexedDB</strong> is for!
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-nosql">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        The first thing you'll notice about IndexedDB is that there's no SQL.
        So we'll refer to it as a "NoSQL" database.
      </p>
      <p>
        Instead of using SQL, you query an
        <span class="slide" id="index-definition">index</span>,
        obtain a <span class="slide" id="cursor-definition">cursor</span>,
        and iterate on the
        <span class="slide" id="result-set-definition">result set</span>.
      </p>
      <p class="index-state">
        An index is a structure that speeds up the retrieval of data
        retrieval operations.
      </p>
      <p class="cursor-state">
        A cursor is a control structure that enables traversal over a database
        result. They are the database equivalent of iterators.
      </p>
      <p class="result-set-state">
        The result set is the collection of objects that matched a particular
        query.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-browser-support">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        Currently only Chrome and Firefox have implemented IndexedDB,
        however, most of the major browser vendors have indicated an intention
        to support it.
      </p>
      <p>
        Today, it's supported via vendor prefixes. Let's simplify this:
      </p>
      <pre class="prettyprint">
window.indexedDB =
  window.indexedDB ||
  window.webkitIndexedDB ||
  window.mozIndexedDB;</pre>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-open">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        We'll be using an asynchronous API to access our IndexedDB database.
        That means there's lots of callbacks. For instance, here's how to open
        a new database:
      </p>
      <pre class="prettyprint">
var request = indexedDB.open(
  "tickets", "Ticket data within."
);
request.onsuccess = function(e) {
  var database = e.target.result;
};</pre>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-setversion">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        IndexedDB has a versioned schema. You can only make changes to the
        schema within a <code class="prettyprint">setVersion</code> request.
      </p>
      <pre class="prettyprint smaller">
var version = "1.0";
var migrationRequest = database.setVersion(version);
migrationRequest.onsuccess = function(e) {
  // Schema changes are done here.
};</pre>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-object-store">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        Instead of tables, IndexedDB has object stores. Let's create one for
        airline tickets. Remember, this can only be done within a
        <code class="prettyprint">setVersion</code> request.
      </p>
      <pre class="prettyprint smaller">
var ticketStore = database.createObjectStore("ticket",
  {keyPath: "ticketNumber"}
);</pre>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-keypath">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        Similar to a primary key, IndexedDB needs a
        <code class="prettyprint">keyPath</code> that tells it what value on
        each object to use as the object's unique lookup key.
      </p>
      <pre class="prettyprint">
{
  <span class="indexeddb-keypath-state">"ticketNumber": "1234567890"</span>,
  "confirmation": "ABCDEF",
  "airline": "Worldwide Airways"
}</pre>
    </div>
  </section>

  <section class="slide deck-after" id="exercise-1">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <h1>Exercise #1</h1>
      <p>
        You need to create an object store named 'ticket' with a keyPath
        of ticketNumber.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-create-index">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        While we're within a <code class="prettyprint">setVersion</code>
        request, we can also create an index on the confirmation value:
      </p>
      <pre class="prettyprint">
ticketStore.createIndex(
  "confirmation", "confirmation", {unique: true}
);</pre>
    </div>
  </section>

  <section class="slide deck-after" id="exercise-2">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <h1>Exercise #2</h1>
      <p>
        The <code class="prettyprint">ticketNumber</code> field will be unique
        because it's the primary key, but confirmations are unique as well.
        Add an index that will ensure the uniqueness of the confirmation.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-transaction">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        Before we can query the database, we're going to need to add something
        for us to query. And before we can do that, we need to create
        a transaction.
      </p>
      <pre class="prettyprint">
var transaction = database.transaction(
  ["ticket"],
  IDBTransaction.READ_WRITE,
  0
);</pre>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-put">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        Now that we have our transaction, lets make a request to put our
        ticket into the object store.
      </p>
      <pre class="prettyprint smaller">
var request = transaction.objectStore("ticket").put({
  "ticketNumber": "1234567890",
  "confirmation": "QRXTFC",
  "airline": "Worldwide Airways"
});</pre>
      <p>
        Unlike SQL databases, you can use <code class="prettyprint">put</code>
        to both create and update data.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-requests">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        Like all other IndexedDB asynchronous requests, we can set
        <code class="prettyprint">onsuccess</code> and/or
        <code class="prettyprint">onfailure</code> callbacks.
      </p>
      <pre class="prettyprint">
request.onsuccess = function(e) {
  // Great our ticket was stored in the database!
}
request.onfailure = function(e) {
  // Ooops, something's gone horribly wrong!
}</pre>
    </div>
  </section>

  <section class="slide deck-after" id="exercise-3">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <h1>Exercise #3</h1>
      <p>
        Take the ticket object and add it to the database.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-cursors">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        OK, great! Now let's try to get our ticket back out again, shall we?
        Make a new transaction, just like before, and then:
      </p>
      <pre class="prettyprint smaller">
var ticketStore = transaction.objectStore("ticket");
// Get everything in the object store;
var keyRange = IDBKeyRange.lowerBound(undefined);
var cursorRequest = ticketStore.openCursor(keyRange);</pre>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-iterating-results">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        That last one seemed a bit scary. But it's not so bad, now we have a
        cursor that we can use to iterate over the tickets. This callback is
        called once per result.
      </p>
      <pre class="prettyprint">
cursorRequest.onsuccess = function(e) {
  var result = e.target.result;
  if(!!result == false) return;
  var ticket = result.value;
  // Do something with it!
  result.continue();
};</pre>
    </div>
  </section>

  <section class="slide deck-after" id="exercise-4">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <h1>Exercise #4</h1>
      <p>
        Remove the example ticket boilerplate from the init method, and
        instead, query the database for the ticket data. Use the supplied
        <code class="prettyprint">ticketr.buildTicketElement</code> function
        to generate each ticket DOM structure.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="indexeddb-deleting">
    <hgroup>
      <h1>Offline Apps</h1>
      <h2>Let's take this offline.</h2>
    </hgroup>
    <div>
      <p>
        Alright, but let's say we don't want that ticket anymore, how do we
        get rid of it? Turns out it's super, super easy!
      </p>
      <pre class="prettyprint smaller">
var ticketNumber = "1234567890";
var ticketStore = transaction.objectStore("ticket");
var deleteRequest = ticketStore.delete(ticketNumber);</pre>
      <p>
        And then just like we've been doing all along, write some callbacks!
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="mobile-intro">
    <hgroup>
      <h1>Mobile Apps</h1>
      <h2>Look Mom! No wires!</h2>
    </hgroup>
    <div>
      <p>
        Great, now that our app works offline, let's think about making it
        work on phones and other devices!
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="mobile-viewport">
    <hgroup>
      <h1>Mobile Apps</h1>
      <h2>Look Mom! No wires!</h2>
    </hgroup>
    <div>
      <p>
        Most smartphones don't attempt to squeeze an entire web page onto
        the screen all at once. Instead they have a viewport, which is a bit
        like reading a newspaper through a magnifying glass.
      </p>
      <p>
        This works great for pages that aren't designed for mobile, but we
        want to make our design look great on phones! This code snippet will
        constrain the viewport to match the device size:
      </p>
      <pre class="prettyprint smaller">
&lt;meta name="viewport"
      content="width=device-width,initial-scale=1.0" /&gt;</pre>
    </div>
  </section>

  <section class="slide deck-after" id="exercise-5">
    <hgroup>
      <h1>Mobile Apps</h1>
      <h2>Look Mom! No wires!</h2>
    </hgroup>
    <div>
      <h1>Exercise #5</h1>
      <p>
        Constrain your viewport to match the device size!
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="mobile-media-queries-intro">
    <hgroup>
      <h1>Mobile Apps</h1>
      <h2>Look Mom! No wires!</h2>
    </hgroup>
    <div>
      <p>
        Great, now we can start making our app look good on smaller devices!
        CSS3 allows use specify that certain CSS rules should only apply to
        device configurations with certain properties.
      </p>
      <p>
        Device height and width are the properties we're going to care about
        most.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="mobile-media-queries-code">
    <hgroup>
      <h1>Mobile Apps</h1>
      <h2>Look Mom! No wires!</h2>
    </hgroup>
    <div>
      <p>
        A good device width to target is
        <code class="prettyprint">480px</code>. This will typically cover most
        smartphones and other devices with very constrained screen sizes.
      </p>
      <pre class="prettyprint">
@media screen and (max-device-width: 480px) {
  /* CSS rules for smartphones go here. */
}</pre>
    </div>
  </section>

  <section class="slide deck-after" id="mobile-media-queries-properties">
    <hgroup>
      <h1>Mobile Apps</h1>
      <h2>Look Mom! No wires!</h2>
    </hgroup>
    <div>
      <p>
        You can also try querying on:
      </p>
      <ul>
        <li><code class="prettyprint">max-width</code></li>
        <li><code class="prettyprint">max-height</code></li>
        <li><code class="prettyprint">min-width</code></li>
        <li><code class="prettyprint">min-height</code></li>
        <li><code class="prettyprint">max-device-pixel-ratio</code></li>
        <li><code class="prettyprint">min-device-pixel-ratio</code></li>
        <li><code class="prettyprint">orientation:portrait</code></li>
        <li><code class="prettyprint">orientation:landscape</code></li>
      </ul>
      <p>
        Note that not all devices support all properties.
        Testing is recommended.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="mobile-common-devices">
    <hgroup>
      <h1>Mobile Apps</h1>
      <h2>Look Mom! No wires!</h2>
    </hgroup>
    <div>
      <p>
        CSS Tricks has a great list of media queries for common devices:
        <h1>
          <a href="http://css-tricks.com/snippets/css/media-queries-for-standard-devices/">
            http://goo.gl/87dRo
          </a>
        </h1>
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="exercise-6">
    <hgroup>
      <h1>Mobile Apps</h1>
      <h2>Look Mom! No wires!</h2>
    </hgroup>
    <div>
      <h1>Exercise #6</h1>
      <p>
        Our current layout is really optimized for desktops and maybe tablets.
      </p>
      <p>
        Adjust the UI to work on smartphones by adding CSS rules to move the
        edit section into a modal view when there isn't enough screen area to
        support a side-by-side layout.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="chrome-apps-intro">
    <hgroup>
      <h1>Chrome Apps</h1>
      <h2>Distribution is key.</h2>
    </hgroup>
    <div>
      <p>
        Great, so now we've got an application that can work offline, and it
        looks nice on mobile phones, now let's get it into the
        Chrome Web Store!
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="chrome-apps-appmator">
    <hgroup>
      <h1>Chrome Apps</h1>
      <h2>Distribution is key.</h2>
    </hgroup>
    <div>
      <p>
        For most apps, you can get them packaged up for distribution in
        a matter of a minute or two. Just go to
        <a href="http://appmator.appspot.com/">appmator.appspot.com</a>!
      </p>
      <p>
        Because we're using the application cache, you may want to request
        the <code class="prettyprint">"unlimitedStorage"</code> permission.
        Just paste it in manually to the <code>manifest.json</code> file.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="exercise-7">
    <hgroup>
      <h1>Chrome Apps</h1>
      <h2>Distribution is key.</h2>
    </hgroup>
    <div>
      <h1>Exercise #7</h1>
      <p>
        Package up your application! Be sure you're requesting unlimited
        storage!
      </p>
      <p>
        Then install it into your local Chrome browser as an unpacked
        application! You can do this from the
        <strong>chrome://extensions</strong> page.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="recap">
    <hgroup>
      <h1>We're Done</h1>
      <h2>That's a wrap!</h2>
    </hgroup>
    <div>
      <p>
        Well, we sure learned a lot there. Quick recap:
      </p>
      <p>
        First, we figured out how to cache all our static resources so they
        could be used while offline. Then we learned how to use IndexedDB to
        store dynamic data on the client.
      </p>
      <p>
        Then we learned how to build layouts that adapt to multiple device
        sizes.
      </p>
      <p>
        And finally, we found out how to package up our application for the
        Chrome Web Store.
      </p>
    </div>
  </section>

  <section class="slide deck-after" id="the-end">
    <hgroup>
      <h1>We're Done</h1>
      <h2>That's a wrap!</h2>
    </hgroup>
    <div>
      <h1>The End</h1>
      <p>
        Not really. Just the beginning, because now you get to go build
        <strong>awesome</strong> applications for the web!
      </p>
    </div>
  </section>
</article>

<footer>
  <p class="g-africa-logo">
    <span class="green bold">g</span>
    <span class="gray left-border">africa</span>
    <span class="lightgray">2.0</span>
  </p>
  <p class="deck-status">
    <span class="deck-status-current">0</span>
    /
    <span class="deck-status-total">0</span>
  </p>
</footer>

<script src="js/jquery.js"></script>
<script>window.jQuery || document.write('<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js">\x3C/script>')</script>

<script src="core/deck.core.js"></script>
<script src="extensions/goto/deck.goto.js"></script>
<script src="extensions/navigation/deck.navigation.js"></script>
<script src="extensions/status/deck.status.js"></script>
<script src="extensions/hash/deck.hash.js"></script>
<script src="slides.js"></script>

</body>
</html>
